package org.snackframework.core;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.snackframework.util.PathUtils;

public class ViewClassLoader extends ClassLoader
{
	private static Log log = LogFactory.getLog(ViewClassLoader.class);
	
	public ViewClassLoader()
	{
		super();
	}
	
	public ViewClassLoader(ClassLoader parent)
	{
		super(parent);
	}
	
	/**
	 * cache class
	 * key:		类的全名,如:org.snack.core.ViewType
	 * value:	key只对应的 Class 实例
	 */
	private static Map<String,Class> classCache=new HashMap<String,Class>(256);
	
	static 
	{
		classCache.put("org.snackframework.core.DispatcherServlet", ViewClassLoader.class);
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException
	{
		int index=name.indexOf("classes");
		String temp=name.substring(index);
		temp=temp.substring(temp.indexOf('/')+1);
		String className=temp.substring(0,temp.indexOf('.'));
		className=StringUtils.replaceChars(className, '/', '.');
		
		Class claz=findLoadedClass(className);
		Class clazz=classCache.get(className);
		if(claz!=null)
		{
			if(null==clazz)
			{
				classCache.put(className, claz);
			}
			return claz;
				
		}
		if(null!=clazz)
		{
			return clazz;
		}
		
		byte[] data = loadClassBytes(name);
		log.info("load "+className);
		clazz=this.defineClass(className, data, 0, data.length);
		classCache.put(className, clazz);
		
		return clazz;
	}
	
	protected Class<?> findClass(File file) throws ClassNotFoundException
	{
		String path=PathUtils.getFilePath(file);
		return findClass(path);
	}
	
	protected byte[] loadClassBytes(String path)
	{
		int size;
		byte[] buffer=new byte[1024];
		byte[] classBytes=null;
		FileInputStream fis=null;
		ByteArrayOutputStream baos=null;
		try
		{
			
			fis=new FileInputStream(path);
			baos=new ByteArrayOutputStream();
			while(-1!=(size=fis.read(buffer)))
			{
				baos.write(buffer, 0, size);
			}
			classBytes = baos.toByteArray();
			if(null!=fis)
			{
				try
				{
					fis.close();
				}
				catch(IOException e){}
			}
			if(null!=baos)
			{
				try
				{
					baos.close();
				}
				catch(IOException e){}
			}
		}
		catch(FileNotFoundException e)
		{
			log.error("", e);
			e.printStackTrace();
		}
		catch(IOException e)
		{
			log.error("",e);
			e.printStackTrace();
		}
		
		return classBytes;
	}
	
	/**
	 * Format : org/snackframework/annotation/Controller.class
	 * 
	 * @param className
	 */
	protected void loadFrameworkClass(String[] jarClassFiles,String jarPath)
	{
		JarFile jarFile;
		if(null!=jarClassFiles)
		{
			try
			{
				jarFile=new JarFile(jarPath);
				for(String jarClassFile : jarClassFiles)
				{
					String className=jarClassFile.substring(0,jarClassFile.indexOf('.'));
					className=StringUtils.replaceChars(className, '/', '.');
					Class clazz=this.classCache.get(className);
					if(null!=clazz)
					{
						continue;
					}
					ByteArrayOutputStream baos=new ByteArrayOutputStream();
					JarEntry entry=jarFile.getJarEntry(jarClassFile);
					InputStream is=jarFile.getInputStream(entry);
					int size;
					byte[] buffer=new byte[1024];
					while((size=is.read(buffer))!=-1)
					{
						baos.write(buffer, 0, size);
					}
					byte[] data=baos.toByteArray();
					
					clazz=defineClass(className, data, 0, data.length);
					if(!clazz.isInterface())
						resolveClass(clazz);
					
					is.close();
					baos.close();
				}
			}
			catch(IOException e)
			{
				if(log.isDebugEnabled())
					e.printStackTrace();
				log.error("访问 "+jarPath+" 出现错误",e);
			}
		}
	}

	/**
	 * load all class file which is in the specified jar
	 * 
	 * @param jarClassFiles
	 */
	protected void loadFrameworkClass(String[] jarPaths)
	{
		for(String jarPath : jarPaths)
		{
			try
			{
				JarFile jarFile=new JarFile(jarPath);
				Enumeration<JarEntry> entries=jarFile.entries();
				while(entries.hasMoreElements())
				{
					JarEntry entry = entries.nextElement();
					if(entry.isDirectory())
					{
						continue;
					}
					else
					{
						String className = entry.getName();
						int index = className.indexOf(".class");
						if(-1!=index)
						{
							className=className.substring(0,index);
							className=StringUtils.replace(className, "/", ".");
							Class clazz=this.classCache.get(className);
							if(null!=clazz)
							{
								continue;
							}
							
							ByteArrayOutputStream baos=new ByteArrayOutputStream();
							InputStream is=jarFile.getInputStream(entry);
							int size;
							byte[] buffer=new byte[1024];
							while((size=is.read(buffer))!=-1)
							{
								baos.write(buffer, 0, size);
							}
							byte[] data=baos.toByteArray();
							
//							clazz=defineClass(className, data, 0, data.length);
							clazz=loadClass(className);
							
							if(!clazz.isInterface())
								resolveClass(clazz);
							
							this.classCache.put(className, clazz);
							
							is.close();
							baos.close();
						}
					}
				}
			}
			catch(ClassNotFoundException e)
			{
				e.printStackTrace();
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}
